//
// Created by 陈成 on 2019/9/19.
//

#include "Kernel.h"

Kernel::Kernel(RegBank<REG_NUM> *r, RegBank<CSR_NUM> *csr, Mem *m) :
        r(r),
        csr(csr),
        m(m) {

}

Kernel::~Kernel() {
    for (int i = 0; i < insts.size(); ++i) {
        delete insts[i];
    }
}

void
Kernel::loadFromFile(const char *filename) {
    FILE *fp = fopen(filename, "r");
    assert(fp != nullptr);

    uint32_t inst, pc = 0;
    while(fscanf(fp, "%x", &inst) != EOF) {
        Inst *i = preProcessInst(pc, sc_bv<32>(inst));
        assert(i != nullptr);
        insts.push_back(i);
        pc += 4;
    }

    fclose(fp);
}

void
Kernel::exec() {
    exec(0);
}

void
Kernel::exec(uint32_t pc) {
    uint32_t pc_nxt = insts[pc>>2]->exec();
    if (pc_nxt < (insts.size() * 4))
        exec(pc_nxt);
}

Inst *
Kernel::preProcessInst(uint32_t pc, sc_bv<32> inst) {
    Inst *i;

    sc_bv<7> opcode = inst.range(6, 0);
    switch (opcode.to_uint()) {
    case 0b0000011:
    case 0b0000111:
    case 0b0011011:
    case 0b0100011:
    case 0b0101011:
    case 0b0101111:
    case 0b0110011:
        i = new InstI(pc, inst, r, csr, m);
        break;
    case 0b0001011:
    case 0b0001111:
        i = new InstU(pc, inst, r, csr, m);
        break;
    case 0b0010011:
        i = new InstR(pc, inst, r, csr, m);
        break;
    case 0b0010111:
        i = new InstJ(pc, inst, r, csr, m);
        break;
    case 0b0011111:
        i = new InstB(pc, inst, r, csr, m);
        break;
    case 0b0100111:
        i = new InstS(pc, inst, r, csr, m);
        break;
    default:
        assert(false);
        break;
    }

    return i;
}
